package org.example.conrtoller;

import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.example.Imp.*;
import org.example.domain.*;
import org.example.dto.ExamPaperDTO;
import org.example.dto.ExamStudentDTO;
import org.example.pageParam.CommonData;
import org.example.vo.ExamBindClassVO;
import org.example.vo.QuestionVO;
import org.example.vo.StudentExamAnswerVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Controller
@RequestMapping("/exam/")

public class ExamControllerT {
    @Autowired
    private QuestionServiceImp questionServiceImp;
    @Autowired
    private TemplateServiceImp templateServiceImp;
    @Autowired
    private Student_ExamServiceImp student_examServiceImp;
    @Autowired
    private StudentServiceImp studentServiceImp;
    @Autowired
    private ExamServiceImp examServiceImp;
    @RequestMapping("mainPage.html")
    public String mainPage(@RequestParam Map<String,Object> map, HttpSession httpSession, Model model){
        Teacher teacher = (Teacher) httpSession.getAttribute("loginTeacher");
        map.put("tid",teacher.getId());
        model.addAttribute("pageVO",  examServiceImp.queryExam(CommonData.PAGE,CommonData.COUNT,map));
        return "exam/mainExamPage";
    }
    @RequestMapping("addExam.html")
    public String addExam(String id,Model model){
        if (id!=null){
            model.addAttribute("exam_id",id);
        }
        return "exam/addExamForm";
    }
    @ResponseBody
    @RequestMapping("addSave")
    public boolean addSave(String name,HttpSession httpSession){
        Exam exam = new Exam();
        exam.setName(name);
        Teacher teacher = (Teacher) httpSession.getAttribute("loginTeacher");
        exam.setTid(Long.parseLong(teacher.getId().toString()));
        exam.setStatus("未发布");
        return examServiceImp.addSave(exam);
    }
    @RequestMapping("query.html")
    public String query(@RequestParam Map<String,Object> map,Model model){
        if (map.get("page")==null){
            map.put("page", CommonData.PAGE);
        }
        map.put("tid",1);
        String start_time = map.get("start_time").equals("")?null:(String) map.get("start_time");
        map.put("start_time",start_time);
        String end_time = map.get("end_time").equals("")?null:(String) map.get("end_time");
        map.put("end_time",end_time);

        model.addAttribute("pageVO",  examServiceImp.queryExam(Integer.parseInt((String) map.get("page")),CommonData.COUNT,map));
        return "exam/mainExamPage::#exam_part_two";
    }
    @RequestMapping("edit.html")
    public String edit(Long id,Model model,HttpSession httpSession){
        Exam exam = examServiceImp.selectExamById(id);
        model.addAttribute("exam",exam);
        return "exam/examEdit";
    }
    @ResponseBody
    @RequestMapping("editName")
    public boolean editName(Long id,String name){
        return examServiceImp.updateExamName(id,name);
    }
    @ResponseBody
    @RequestMapping("bindStudentClass")
    public void bindStudentClass(String examId,String studentClass,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        if (map==null){
            map = new HashMap<>();
            map.put(studentClass,"All");
            httpSession.setAttribute(key,map);
        }else {
            map.put(studentClass,"All");
        }

    }
    @ResponseBody
    @RequestMapping("unbindStudentClass")
    public void unbindStudentClass(String examId,String studentClass,HttpSession httpSession){
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute("bindStudentClass"+examId);
        map.remove(studentClass);
    }
    @ResponseBody
    @RequestMapping("bindAllAndUnbindAll")
    public void bindAllAndUnbindAll(String examId,String studentClasses,String status,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        String[] tempClassInfo = studentClasses.split(",");//2020-软件-1班,2020-软件-2班
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        if (status.equals("绑定")){
            if (map==null){
                map = new HashMap<>();
                for (String temp:tempClassInfo){
                    map.put(temp,"All");
                }
                httpSession.setAttribute(key,map);
            }else {
                for (String temp:tempClassInfo){
                    map.put(temp,"All");
                }
            }
        }else {
            for (String temp:tempClassInfo){
                map.remove(temp);
            }
        }
    }
    @ResponseBody
    @RequestMapping("bindStudent")
    public void bindStudent(String examId,String studentClass,String studentNumbers,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        if (map==null){
            map = new HashMap<>();
            map.put(studentClass,studentNumbers);//studentNumbers 202000011,202000022学号
            httpSession.setAttribute(key,map);
        }else {
            map.put(studentClass,studentNumbers);
        }
    }
    @ResponseBody
    @RequestMapping("unbindStudent")
    public void unbindStudent(String examId,String studentClass,String studentNumbers,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        String students = map.get(studentClass);//202000011,202000022,202000033  也有可能是 All
        if (students.equals("All")){
            //需要从数据库中查找出所有的学生学号
            students = studentServiceImp.selectStudentCode(studentClass,studentNumbers);
        }

        String[] tempStudentNumbers = students.split(",");
        StringBuilder resultStudentNumbers = new StringBuilder();
        for (String temp:tempStudentNumbers){
            if (temp.equals(studentNumbers)){
                continue;
            }else {
                resultStudentNumbers.append(temp+",");
            }
        }
        map.put(studentClass,resultStudentNumbers.toString());
    }
    @RequestMapping("selectCacheClass")
    @ResponseBody
    public Map<String,String> selectCacheClass(String examId,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        return (Map<String, String>) httpSession.getAttribute(key);
    }
    @RequestMapping("defaultShowBindClass.html")
    public String defaultShowBindClass(String examId,HttpSession httpSession,Model model){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        List<ExamBindClassVO> list = new ArrayList<>();
        if (map==null){
            map = new HashMap<>();
            httpSession.setAttribute(key,map);
            //当前缓存中没有当前考试模板的班级缓存   但是数据库中可能存储了这场考试的信息
            List<ExamBindClassVO> list1 = examServiceImp.selectBindClassByExamId(Long.parseLong(examId));
            if (list1.size()==0){
                model.addAttribute("bindStudentClasses",null);
                return "exam/examEdit::#exam_edit_table";
            }
            //到这里就表示数据库存在当前考试模板的关联信息 但是有几个问题 什么呢？
            //首先list1中的对象信息不完全 需要自己做逻辑处理
            //第一次打开这个缓存中没有map  走这条分支  可以显示数据  下一次再打开这个模板  缓存中就有map了 会走map分支获取数据(但是map中没有数据)
            //所以当我们从数据库获取到当前班级数据时，就需要存储到缓存中

            //首先解决vo中的参数问题
            //从数据库只获取到className  selectStudentNumber classStudentTotal
            //存在的班级正常查询  当存在一个自定义班级时   selectStudentNumber classStudentTotal 这两个需要一致
            //还有一个status  自定义 和 非自定义
            for (int i = 0;i<list1.size();i++){
                ExamBindClassVO examBindClassVO = list1.get(i);
                if (examBindClassVO.getClassStudentTotal()==0){
                    //表示为一个自定义班级
                    examBindClassVO.setClassStudentTotal(examBindClassVO.getSelectStudentNumber());
                    examBindClassVO.setStatus("自定义");
                    //需要转换学号
                    String[] ids = examBindClassVO.getCodes().split(",");
                    Long[] temp = new Long[ids.length];
                    for (int j = 0;j<temp.length;j++){
                        temp[j] = Long.parseLong(ids[j]);
                    }
                    List<Student> studentList = studentServiceImp.selectStudentByIds(Arrays.asList(temp));
                    StringBuilder stringBuilder = new StringBuilder();

                    for (int x = 0;x<studentList.size();x++){
                        Student student = studentList.get(x);
                        stringBuilder.append(student.getCode()+",");
                    }
                    examBindClassVO.setCodes("x,"+stringBuilder.toString());
                }else {
                    examBindClassVO.setStatus("非自定义");
                    //需要做一个处理  codes存储的是学生id 但是缓存中需要的学生学号  还需要判断selectStudentNumber classStudentTotal是否相等
                    if (examBindClassVO.getClassStudentTotal()== examBindClassVO.getSelectStudentNumber()){
                        //班级总人数 和 参数人数是否相等
                        examBindClassVO.setCodes("All");
                    }else {
                        String[] ids = examBindClassVO.getCodes().split(",");
                        Long[] temp = new Long[ids.length];
                        for (int j = 0;j<temp.length;j++){
                            temp[j] = Long.parseLong(ids[j]);
                        }
                        List<Student> studentList = studentServiceImp.selectStudentByIds(Arrays.asList(temp));
                        StringBuilder stringBuilder = new StringBuilder();

                        for (int x = 0;x<studentList.size();x++){
                            Student student = studentList.get(x);
                            stringBuilder.append(student.getCode()+",");
                        }
                        examBindClassVO.setCodes(stringBuilder.toString());
                    }
                }
                //到这里 一个VO就处理好了  存储到map就可以了
                map.put(examBindClassVO.getClassName(),examBindClassVO.getCodes());
            }

            model.addAttribute("bindStudentClasses",list1);

        }
        Set<String> set = map.keySet();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            ExamBindClassVO examBindClassVO = new ExamBindClassVO();
            String tempKey = iterator.next();
            examBindClassVO.setClassName(tempKey);
            String tempValue = map.get(tempKey);
            if (tempValue.equals("All")){
                //只需要获取这个班级的人数即可
                int studentTotal = studentServiceImp.selectClassStudentTotalNumber(tempKey);
                examBindClassVO.setSelectStudentNumber(studentTotal);
                examBindClassVO.setClassStudentTotal(studentTotal);
                examBindClassVO.setStatus("非自定义");
            }else if (tempValue.startsWith("x")){
                //自定义班级
                String[] tempDefinedClass = tempValue.split(",");//x,1,2,3  x代表为自定义班级 后面都是学号
                examBindClassVO.setSelectStudentNumber(tempDefinedClass.length-1);
                examBindClassVO.setClassStudentTotal(tempDefinedClass.length-1);
                examBindClassVO.setStatus("自定义");
            }else {
                //表示需要获取当前班级中学生 然后进行比对  tempValue:20200011,20200022
                String[] tempStudentCode = tempValue.split(",");
                examBindClassVO.setSelectStudentNumber(tempStudentCode.length);
                examBindClassVO.setClassStudentTotal(studentServiceImp.selectClassStudentTotalNumber(tempKey));
                examBindClassVO.setStatus("非自定义");
            }

            list.add(examBindClassVO);
        }
        model.addAttribute("bindStudentClasses",list);
        model.addAttribute("exam",examServiceImp.selectExamById(Long.parseLong(examId)));
        return "exam/examEdit::#exam_edit_table";
    }
    @RequestMapping("definedClass.html")
    public String definedClass(){
        return "exam/definedClass";
    }
    @ResponseBody
    @RequestMapping("definedClassSave")
    public boolean definedClassSave(String examId,String definedClassName,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        if (map==null){
            map = new HashMap<>();
            map.put(definedClassName,"x,");
            httpSession.setAttribute(key,map);
            return true;
        }else {
            Set<String> set = map.keySet();
            Iterator<String> iterator = set.iterator();
            int symbol = 0 ;
            while (iterator.hasNext()){
                String tempKey = iterator.next();
                if (tempKey.equals(definedClassName)){
                   symbol++;
                   break;
                }
            }
            if (symbol==0){
                map.put(definedClassName,"x,");
                return true;
            }else {
                return false;
            }
        }
    }
    @RequestMapping("importClass.html")
    public String importClassPage(){
        return "exam/examImportClass";
    }
    @RequestMapping("downloadTemplate.xlsx")
    public ResponseEntity<byte[]> downloadTemplate(){
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("downloadTemplate/studentDemo.xlsx");
        byte[] bytes = null;
        try {
            bytes = new byte[inputStream.available()];
            IOUtils.read(inputStream,bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        HttpHeaders httpHeaders = new HttpHeaders();//这里是为了返回xlsx时 出现下载框 否则默认展示在页面中
        httpHeaders.add("content-disposition","attachment;filename=students.xlsx");
        httpHeaders.add("content-type","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        return new ResponseEntity<byte[]>(bytes,httpHeaders, HttpStatus.OK);
    }
    @ResponseBody
    @RequestMapping(value = "importClassSave",produces = {"text/html;charset=UTF-8"})
    public String importClassSave(MultipartFile file,String examId,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        if (map==null){
            map = new HashMap<>();
            httpSession.setAttribute(key,map);
        }
        StringBuilder resultParam  = new StringBuilder();
        int successCount = 0;
        int failedCount = 0;
        //解析file
        try {
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
            //上述默认是处理excel中第一张表数据   我们要第一张后面的数据 所以需要poi原生的操作
            int sheetCount = reader.getSheetCount();
            for (int i = 1;i<reader.getSheetCount();i++){
                reader.setSheet(i);
                Sheet currentSheet = reader.getSheet();
                String className = currentSheet.getSheetName();
                //需要做什么呢？ 首先需要判断当前表格中的学生是否在数据库中真实存在 如果存在拼接学号 如果不存在给出提示 然后 判断班级是否存在 如果存在 直接存储  不存在 则是自定义班级 map中的value以x开头
                //如何判断学生是否存在呢？ 一个一个与数据库对比？（性能太慢） 我们可以将一个sheet表中所有的学生信息拼接在一起 然后利用mysql语句中的in判断
                //拼接规则  姓名-学号,姓名-学号，姓名-学号
                StringBuilder allStudentInfo = new StringBuilder();
                j:for (int j = 1;j<=currentSheet.getLastRowNum();j++){
                    Row row = currentSheet.getRow(j);
                    if (row==null){//说明当前row已经是尾巴了
                        continue j;
                    }
                    Double code = row.getCell(0).getNumericCellValue();
                    DecimalFormat decimalFormat = new DecimalFormat();//处理了excel中数字大变成16进制的问题
                    String realCode = decimalFormat.format(code);//2,22,22这种形式 需要将， 变为“”
                    if (realCode!=null && realCode.indexOf(",")>=0){
                        realCode = realCode.replaceAll(",","");
                    }
                    Cell name = row.getCell(1);
                    String codeAndName = name.toString()+"-"+realCode+",";
                    allStudentInfo.append(codeAndName);
                }
                allStudentInfo.replace(allStudentInfo.length()-1,allStudentInfo.length(),"");//xxx-202011001,xxx-202011002,xxx-202011003
                List<Student> currentClassInfo = studentServiceImp.selectStudentByNameAndCode(Arrays.asList(allStudentInfo.toString().split(",")));
                //上述的student都是数据库中存在的  下面就是作比较了 将stringBuilder中的数据拆分判断
                String[] studentInfos = allStudentInfo.toString().split(",");
                StringBuilder cacheMapValue = new StringBuilder();
                x:for (int x = 0;x<studentInfos.length;x++){
                    String splitCode = studentInfos[x].split("-")[1];
                    boolean exist = false;
                    y:for (int y = 0;y<currentClassInfo.size();y++){
                        Student student = currentClassInfo.get(y);
                        String code = student.getCode();
                        if (code.equals(splitCode)){
                            //说明当前学生存在于数据库中
                            cacheMapValue.append(splitCode+",");
                            successCount++;
                            exist = true;
                            break y;
                        }
                    }
                    if (exist==false){
                        //表示当前student不存在于excel中
                        resultParam.append(splitCode+"学号不存在|");
                        failedCount++;
                    }
                }
                //判断当前班级是否存在
                int count = studentServiceImp.judgeClassExist(className);
                if (count==0){
                    //表示班级不存在  就是自定义班级
                    map.put(className,"x,"+cacheMapValue.toString());
                }else {
                    map.put(className,cacheMapValue.toString());//直接覆盖
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "总共导入:"+(successCount+failedCount)+";"+"成功:"+successCount+"个;"+"失败:"+failedCount+"个;失败信息如下："+resultParam.toString();
    }
    @ResponseBody
    @RequestMapping("removeClass")
    public void removeClass(String examId,String className,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        map.remove(className);
    }
    @RequestMapping("importClassStudent.html")
    public String importClassStudent(){
        return "exam/examImportClass";
    }
    @RequestMapping(value = "importClassStudentSave",produces = {"text/html;charset=UTF-8"})
    @ResponseBody
    public String importClassStudentSave(String examId,String className,MultipartFile file,HttpSession httpSession){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        int successCount = 0;
        int failedCount = 0;
        StringBuilder resultParam  = new StringBuilder();
        //解析excel
        try {
            InputStream inputStream = file.getInputStream();
            ExcelReader reader = ExcelUtil.getReader(inputStream);
            int sheetCount = reader.getSheetCount();
            for (int i = 1;i<reader.getSheetCount();i++){
                reader.setSheet(i);
                Sheet sheet = reader.getSheet();
                StringBuilder allStudentInfo = new StringBuilder();
                int rowCount = sheet.getLastRowNum();
                j:for (int j = 1;j<=sheet.getLastRowNum();j++){
                    Row row = sheet.getRow(j);
                    if (row==null){
                        continue j;
                    }
                    Double code = row.getCell(0).getNumericCellValue();
                    DecimalFormat decimalFormat = new DecimalFormat();//处理了excel中数字大变成16进制的问题
                    String realCode = decimalFormat.format(code);//2,22,22这种形式 需要将， 变为“”
                    if (realCode!=null && realCode.indexOf(",")>=0){
                        realCode = realCode.replaceAll(",","");
                    }
                    Cell name = row.getCell(1);
                    String codeAndName = name.toString()+"-"+realCode+",";
                    allStudentInfo.append(codeAndName);
                }
                allStudentInfo.replace(allStudentInfo.length()-1,allStudentInfo.length(),"");//xxx-202011001,xxx-202011002,xxx-202011003
                List<Student> currentClassInfo = studentServiceImp.selectStudentByNameAndCode(Arrays.asList(allStudentInfo.toString().split(",")));
                String[] studentInfos = allStudentInfo.toString().split(",");
                StringBuilder cacheMapValue = new StringBuilder();
                x:for (int x = 0;x<studentInfos.length;x++){//循环走完  存在的学号都在这里   学号,学号,学号  并且获取到成功的个数 和 失败的个数 和 不存在的信息
                    String splitCode = studentInfos[x].split("-")[1];
                    boolean exist = false;
                    y:for (int y = 0;y<currentClassInfo.size();y++){
                        Student student = currentClassInfo.get(y);
                        String code = student.getCode();
                        if (code.equals(splitCode)){
                            //说明当前学生存在于数据库中
                            cacheMapValue.append(splitCode+",");
                            successCount++;
                            exist = true;
                            break y;
                        }
                    }
                    if (exist==false){
                        //表示当前student不存在于excel中
                        resultParam.append(splitCode+"学号不存在|");
                        failedCount++;
                    }
                }
                //不能直接存储 因为当前导入 是基于存在的班级 这个班级可能之前有学生 如果直接拼接 可能会出现相同的学号 例如 1,2,3,4,2
                String currentClassNumbers = map.get(className);//x, + 1,2,3,4    ||   x  + 1,2,3,4,
                //这个请求都是针对于自定义班级  所以  学号前有一个x
                if (!currentClassNumbers.endsWith(",")){
                    currentClassNumbers = currentClassNumbers +",";
                }
                String concatClassNumbers = currentClassNumbers.replace("x,","") + cacheMapValue;
                String[] concatClassNumberArray = concatClassNumbers.split(",");
                HashSet<String> resultSet = new HashSet<>();
                for (int j = 0;j<concatClassNumberArray.length;j++){//去重操作
                    resultSet.add(concatClassNumberArray[j]);
                }
                Iterator<String> iterator = resultSet.iterator();
                StringBuilder stringBuilder = new StringBuilder();
                while (iterator.hasNext()){
                    String next = iterator.next();
                    stringBuilder.append(next+",");
                }
                map.put(className,"x,"+stringBuilder.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "总共导入:"+(successCount+failedCount)+";"+"成功:"+successCount+"个;"+"失败:"+failedCount+"个;失败信息如下："+resultParam.toString();
    }
    @RequestMapping("adjustStudent.html")
    public String adjustStudent(String examId,String className,HttpSession httpSession,Model model){
        //需要默认展示一些数据
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        List<Student> relationStudent ;
        List<Student> noRelationStudent;
        String status ;
        String classNumbers = map.get(className);
        //我们需要查询出当前班级关联的学生信息 和 未关联的学生信息  都是当前班级的学生信息
        //如果是非自定义班级  有两种情况 一个是All  一个学号,学号,学号
        //如果是自定义班级  这次页面默认不展示未关联的学生  需要自己进行按钮查询  这里就查找关联的学生
        if (classNumbers.equals("All")){
            //班级关联所有学生
            relationStudent = studentServiceImp.selectStudentByClassInfo(className);
            noRelationStudent = null;
            status = "非自定义";
        }else if (classNumbers.startsWith("x")){
            //自定义班级
            relationStudent = studentServiceImp.selectStudentByCodes(Arrays.asList(classNumbers.replace("x,","").split(",")));
            noRelationStudent = null;
            status = "自定义";
        }else {
            List<String> codes = Arrays.asList(classNumbers.split(","));
            //班级关联部分学生
            relationStudent = studentServiceImp.selectStudentByCodes(codes);
            noRelationStudent = studentServiceImp.selectNoneExistStudentByCodes(className,codes);
            status = "非自定义";
        }

        model.addAttribute("relationClass",relationStudent);
        model.addAttribute("noneRelationClass",noRelationStudent);
        model.addAttribute("className",className);
        model.addAttribute("status",status);
        //做发布考试 编辑页面处理 需要的
        model.addAttribute("exam",examServiceImp.selectExamById(Long.parseLong(examId)));
        return "exam/adjustStudent";
    }
    @RequestMapping("adjustStudentSearch.html")
    public String adjustStudentSearch(String examId,String currentClassName,String searchClassName,HttpSession httpSession,Model model){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        String currentClassNumbers = map.get(currentClassName);
        List<Student> noneRelationList ;
        //如果这个自定义班级没有关联任何学生那么就是x,  如果关联一个甚至多个 x,学号,学号，.....
        if (currentClassNumbers.length()==2){
            //表示自定义班级没有添加任何学生
            noneRelationList = studentServiceImp.selectNoneExistStudentByCodes(searchClassName,null);
        }else {
            noneRelationList = studentServiceImp.selectNoneExistStudentByCodes(searchClassName,Arrays.asList(currentClassNumbers.replace("x,","").split(",")));
        }
        model.addAttribute("noneRelationClass",noneRelationList);
        //做发布考试 编辑页面处理 需要的
        model.addAttribute("exam",examServiceImp.selectExamById(Long.parseLong(examId)));
        return "exam/adjustStudent::#noneRelationTable";
    }
    @RequestMapping("adjustStudentBind")
    public String adjustStudentBind(String examId,String searchClassName,String className,String currentStudentCode,HttpSession httpSession,Model model){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        List<Student> relationStudent ;
        List<Student> noRelationStudent;
        String currentClassCodes = map.get(className);//因为之前的操作 我忘记了拼接是否以,结尾
        if (currentClassCodes.endsWith(",")){
            currentClassCodes = currentClassCodes + currentStudentCode +",";
        }else {
            currentClassCodes = currentClassCodes + "," + currentStudentCode +",";
        }
        map.put(className,currentClassCodes);
        relationStudent = studentServiceImp.selectStudentByCodes(Arrays.asList(currentClassCodes.replace("x,","").split(",")));
        noRelationStudent = studentServiceImp.selectNoneExistStudentByCodes(searchClassName,Arrays.asList(currentClassCodes.replace("x,","").split(",")));;
        model.addAttribute("relationClass",relationStudent);
        model.addAttribute("noneRelationClass",noRelationStudent);
        //做发布考试 编辑页面处理 需要的
        model.addAttribute("exam",examServiceImp.selectExamById(Long.parseLong(examId)));
        return "exam/adjustStudent::#adjustStudentContent";
    }
    @RequestMapping("adjustStudentUnBind")
    public String adjustStudentUnBind(String examId,String searchClassName,String className,String currentStudentCode,HttpSession httpSession,Model model){
        String key = "bindStudentClass"+examId;
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        List<Student> relationStudent ;
        List<Student> noRelationStudent;
        String currentClassCodes = map.get(className);
        StringBuilder cacheMapValue = new StringBuilder();
        if (currentClassCodes.equals("All")){
            List<Student> studentList = studentServiceImp.selectStudentByClassInfo(className);
            for (int i = 0;i<studentList.size();i++){
                if (!studentList.get(i).getCode().equals(currentStudentCode)){
                    cacheMapValue.append(studentList.get(i).getCode()+",");
                }
            }
            map.put(className,cacheMapValue.toString());
            relationStudent = studentServiceImp.selectStudentByCodes(Arrays.asList(map.get(className).split(",")));
            noRelationStudent = studentServiceImp.selectNoneExistStudentByCodes(className,Arrays.asList(map.get(className).split(",")));
        }else if (currentClassCodes.startsWith("x,")){
            String[] splitNumbers = currentClassCodes.replace("x,", "").split(",");
            for (int i = 0;i< splitNumbers.length;i++){
                if (!splitNumbers[i].equals(currentStudentCode)){
                    cacheMapValue.append(splitNumbers[i]+",");
                }
            }
            map.put(className,"x,"+cacheMapValue.toString());
            relationStudent = studentServiceImp.selectStudentByCodes(Arrays.asList(map.get(className).replace("x,","").split(",")));
            noRelationStudent = studentServiceImp.selectNoneExistStudentByCodes(searchClassName,Arrays.asList(map.get(className).replace("x,","").split(",")));
        }else {
            String[] splitNumbers = currentClassCodes.split(",");
            for (int i = 0;i< splitNumbers.length;i++){
                if (!splitNumbers[i].equals(currentStudentCode)){
                    cacheMapValue.append(splitNumbers[i]+",");
                }
            }
            map.put(className,cacheMapValue.toString());
            relationStudent = studentServiceImp.selectStudentByCodes(Arrays.asList(map.get(className).split(",")));
            noRelationStudent = studentServiceImp.selectNoneExistStudentByCodes(className,Arrays.asList(map.get(className).split(",")));
        }
        model.addAttribute("relationClass",relationStudent);
        model.addAttribute("noneRelationClass",noRelationStudent);
        //做发布考试 编辑页面处理 需要的
        model.addAttribute("exam",examServiceImp.selectExamById(Long.parseLong(examId)));
        return "exam/adjustStudent::#adjustStudentContent";
    }
    @RequestMapping(value = "editSave",produces = {"text/html;charset=UTF-8"})
    @ResponseBody
    public String editSave(Exam exam, HttpSession httpSession){
        String key = "bindStudentClass"+exam.getId();
        Map<String,String> map = (Map<String, String>) httpSession.getAttribute(key);
        Teacher teacher = (Teacher) httpSession.getAttribute("loginTeacher");
        exam.setTid(Long.parseLong(teacher.getId().toString()));
        //考试信息保存的时候需要考虑一些问题 什么呢？
        //exam中的信息都有  直接存储数据库就行了  t_exam
        //考试模板中还有关联的班级学生  在缓存中  需要储存到t_student_exam表中 这里就存在问题了  什么呢？
        //因为我们在做学生的关联时，一个班级的学生是不会存在重复的学生的  但是，如果一场考试关联了多个班级 就会出现学生重复的可能
        //意思就是 A学生可能在1班进行考试  然后自定义班级又出现了这个学生 他不可能同时在两个地方考试吧，所以，在保存exam信息时，需要判断学生的重复
        //不要一上来就直接去重 如果没有那不是很浪费性能（感觉也差不到哪去） 所以先判断是否需要去重
        boolean result = editSaveStudentDistinctJudge(map);
        if (!result){//false 代表没有重复学生 到此为止 可以保存 反之
            //这里有问题 如果这个考试模板时第一次保存 那往数据库存储exam  和 关联学生即可  如果是第二次保存 就会出现数据库关联学生重复的问题
            //解决方法 一次性将当前模板关联的学生删除完(与数据库交互一次 但是一次操作多个数据) 或者 先从数据库查询到当前模板的所有学生 然后进行缓存对比 删除没有的 添加拥有的(多次交互 一次交互处理不多   老师用的这个)
            examServiceImp.editSave(exam,map);
            return "保存成功";
        }
        //表示有重复学生需要删除  如果存在重复 就不用往数据库存储了 直接返回重复信息 直到改掉为止
        return editSaveStudentDistinct(map);
    }
    public String editSaveStudentDistinct(Map<String,String> classInfo){
        //如何去重？
        //比如map中包含了 如下数据：
        //“2020-软件-1班”：“1,2,3,4,5”
        //“2020-计算机-1班”：“6,4,7,8,9”
        //“2021-软件-1班”：“5,10,11,12”
        //很明显4号学生在2020-软件-1班和2020-计算机-1班都出现过  5号学生在2020-软件-1班 和 2021-软件-1班出现过
        //我们想把最后的结果按照{“重复学号”:["重复班级","重复班级"],"重复学号":["重复班级","重复班级"]} 这个形式进行反馈 {key1=[1, 2], key2=[1, 2]}
        //首先我们需要一个装载最终结果的一个map
        Map<String,HashSet<String>> resultMap = new HashMap<>();
        //为了后面获取班级方便 需要将原生的classInfo转化成List<Map<String,HashSet<String>>>
        List<Map<String,HashSet<String>>> changeClassInfo = new ArrayList<>();
        Iterator<String> iterator = classInfo.keySet().iterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            String value = classInfo.get(key);
            Map<String,HashSet<String>> tempMap = new HashMap<>();
            HashSet<String> tempSet = new HashSet<>();
            if (value.equals("All")){
                String classStudentCodes = studentServiceImp.selectClassAllStudentCode(key);
                String[] codes = classStudentCodes.split(",");
                tempSet.addAll(Arrays.asList(codes));
            }else if (value.startsWith("x,")){
                String[] split = value.replace("x,", "").split(",");
                tempSet.addAll(Arrays.asList(split));
            }else {
                tempSet.addAll(Arrays.asList(value.split(",")));
            }
            tempMap.put(key,tempSet);
            changeClassInfo.add(tempMap);

        }
        //创建一个用来装载重复元素的
        HashSet<String> distinctBox = new HashSet<>();
        i:for (int i = 0;i<changeClassInfo.size();i++){
            Map<String, HashSet<String>> map1 = changeClassInfo.get(i);
            String map1Key = map1.keySet().iterator().next();//班级名称
            HashSet<String> map1Values = map1.get(map1Key);//班级学号
            j:for (int j =i+1;j< changeClassInfo.size();j++){
                Map<String, HashSet<String>> map2 = changeClassInfo.get(j);
                String map2Key = map2.keySet().iterator().next();
                HashSet<String> map2Values = map2.get(map2Key);
                distinctBox.addAll(map1Values);
                distinctBox.retainAll(map2Values);
                if (distinctBox.size()==0){
                    //表示当前i班 和 当前j班 不存在重复元素
                    distinctBox.clear();
                    continue j;
                }
                Iterator<String> iterator1 = distinctBox.iterator();
                while (iterator1.hasNext()){
                    String repetitionCode = iterator1.next();
                    //不知道resultMap中是否存储过当前学号
                    HashSet<String> classSet = resultMap.get(repetitionCode);
                    if (classSet==null){
                        HashSet<String> tempClassSet = new HashSet<>();
                        tempClassSet.add(map1Key);
                        tempClassSet.add(map2Key);
                        resultMap.put(repetitionCode,tempClassSet);
                    }else {
                        classSet.add(map1Key);
                        classSet.add(map2Key);
                    }
                }
                distinctBox.clear();
            }
        }
        //到此重复数据就查出来了 需要自己组装数据 map好像没有重写toString
        return "重复信息如下:"+resultMap.toString().replaceAll("=",":");
    }
    public boolean editSaveStudentDistinctJudge(Map<String,String> classInfo){
        //classInfo中的格式为{“班级”:"学号,学号,学号","班级":"All","班级","x,学号,学号,学号"}
        //思路？这里只是简单的判断是否有重复，不需要找到重复元素  所以，这里用老师的思路
        //先想办法获取map中所有班级的一个长度 然后将map中所有的学生存储到一个set集合中 比对两个长度
        List<String> oldLength = new ArrayList<>();
        HashSet<String> newLength = new HashSet<>();
        Set<String> set = classInfo.keySet();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            String key = iterator.next();
            String value = classInfo.get(key);
            //需要解析一下
            if (value.equals("All")){
                String classStudentCodes = studentServiceImp.selectClassAllStudentCode(key);
                String[] codes = classStudentCodes.split(",");
                List<String> temp = Arrays.asList(codes);
                oldLength.addAll(temp);
                newLength.addAll(temp);
            }else if (value.startsWith("x,")){
                String[] split = value.replace("x,", "").split(",");
                List<String> temp = Arrays.asList(split);
                oldLength.addAll(temp);
                newLength.addAll(temp);
            }else {
                List<String> temp = Arrays.asList(value.split(","));
                oldLength.addAll(temp);
                newLength.addAll(temp);
            }
        }
        Boolean result = null;
        if (oldLength.size()== newLength.size()){
            result = false;
        }else {
            result = true;
        }
        return result;
    }

    /**
     * 动态模板的抽取试题 一定要保证数据库试题类型大于等于抽取类型 否则处理试卷的线程会死循环 卡死
     * @param examId
     * @return
     */
    @RequestMapping("generatePaper")
    @ResponseBody
    public boolean generatePaper(Long examId){
        //所谓的生成考卷  就是将student_exam中当前模板关联的学生中 page_path修改 判断模板的类型  动态还是静态 分别生成考卷
        //首先需要判断当前模板的学生是否生成过考卷
        int count = student_examServiceImp.judgeGeneratePage(examId);
        if (count==0){
            //表示当前模板的学生都生成过考卷  不可以重复生成
            return false;
        }
        //没有生成考卷  需要获取考试信息  慢慢处理
        Exam exam = examServiceImp.selectExamById(examId);
        Template template = templateServiceImp.selectTemplateById(exam.getTemplate_id());
        //创建当前考试文件夹
        //首先创建当前考试的文件夹 D:\exam_paper\  这个目录当作考试文件的存放地址
        File file = new File(CommonData.COMMON_EXAM_PATH+exam.getName());
        if (!file.exists()){
            try {
               file.mkdir();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //目的是为了方便获取参数 不用写几个代码块
        List<String> allQuestionList = new ArrayList<>();
        allQuestionList.add(template.getQuestion1());
        allQuestionList.add(template.getQuestion2());
        allQuestionList.add(template.getQuestion3());
        allQuestionList.add(template.getQuestion4());
        allQuestionList.add(template.getQuestion5());
        if (template.getTemplateType().equals("静态模板")){
            //需要做考卷生成  因为是静态模板 所有考生都是使用一套考卷考试  所以产生一个文件  将这个文件路径更新到所有学生中
            File staticPaper = new File(CommonData.COMMON_EXAM_PATH+exam.getName()+"/"+CommonData.COMMON_STATIC_TEMPLATE_PAGE_NAME);
            if (!staticPaper.exists()){
                try {
                    staticPaper.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                return false;
            }
            //需要在当前模板的文件夹下面创建考试静态文件  需要解析当前模板的试题
            //第一题type--
            //第一题score--
            //第一题subject
            //第一题options  (op1}-|-{op2}-|-{op3)--
            //第一题answer   (an1}-|-{an2}-|-{an3)--
            //====--
            //第二题type--
            //第二题score--
            //第二题subject--
            //第二题options  (op1}-|-{op2}-|-{op3)--
            //第二题answer   (an1}-|-{an2}-|-{an3)--
            //上述是老师的解析方法 我用一下

            int number = 0;
            try {
                FileWriter fileWriter = new FileWriter(staticPaper);
                for (int j = 0; j < allQuestionList.size(); j++) {
                    String currentQuestion = allQuestionList.get(j);//2}-|-{376}-|-{377}-|-{378}-|-{ 当前是单选题 第一个2是分值 后面都是考题题号
                    String[] split = currentQuestion.split(CommonData.COMMON_SEPARATOR);
                    String currentScore = split[0];
                    //将考题转化为question
                    Long[] temp = new Long[split.length];
                    for (int x = 1; x < split.length; x++) {
                        temp[x] = Long.parseLong(split[x]);
                    }
                    List<Question> currentQuestionList = questionServiceImp.selectQuestionByIds(Arrays.asList(temp));
                    for (int i = 0; i < currentQuestionList.size(); i++) {
                        ++number;
                        String questionNumber = new StringBuilder("第" + number + "题").toString();
                        Question question = currentQuestionList.get(i);
                        fileWriter.write(questionNumber+"类型:" + question.getType() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                        fileWriter.write(questionNumber+"分值:" + currentScore + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                        fileWriter.write(questionNumber+"题干:" + question.getSubject() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                        fileWriter.write(questionNumber+"选项:" + question.getOptions() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                        fileWriter.write(questionNumber+"答案:" + question.getAnswer() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                        fileWriter.write(CommonData.COMMON_QUESTION_SEPARATOR);
                    }
                }
                fileWriter.flush();
                fileWriter.close();
            }catch (Exception e){
                e.printStackTrace();
            }
            //到此当前考试的静态考卷就生成成功  需要将当前考试的关联考生的page_path修改成考卷存放路径
            String page_path = staticPaper.getAbsolutePath();
            student_examServiceImp.updateStudentPage_Path(examId,page_path);
            return true;
        }else {
            //创建线程池管理线程的提交
            ExecutorService executorService = Executors.newCachedThreadPool();
            //动态模板考卷的生成  首先分析一下 当前考试 关联了一个动态模板 因为动态模板中并没有具体的试题存在 需要单独抽取试题
            //但是 一场考试中 会有很多学生参考  又是动态模板(每一个考生需要产生一个单独的考卷 或者 一个文件) 如果在当前线程中进行生成
            //会很慢  一个线程处理很多学生的考卷   所以我们利用多线程进行考卷生成  一个线程处理部分学生(按照情况分配 电脑cpu的处理器个数)
            //还有一个问题 如果一个学生按照动态模板进行考卷的生成  那么这个学生就需要进行试题的抽取(与数据库交互 简答题 中等题 困难题)的抽取
            //假如一百个学生都需要动态模板的考题抽取  如数据库的交互次数很多 浪费时间(为什么呢？)
            //因为当前动态模板时固定的样式  我们可以一次性查出所有的符合动态模板的考题 存放到内存中 这样就不用访问数据库了
            //首先将当前考试关联的动态模板找到  找到后 需要将符合模板中的试题抽取出来存放
            //抽取动态试题
            Map<String,Map<String,List<Question>>> currentTemplateQuestionCache = new HashMap<>();
            //存储格式 单选题:{"简单":List<Question>;"中等":List<Question>;"困难":List<Question>}
            String[] questionTypes = new String[]{CommonData.COMMON_QUESTION1,CommonData.COMMON_QUESTION2,CommonData.COMMON_QUESTION3,CommonData.COMMON_QUESTION4,CommonData.COMMON_QUESTION5};
            for (int i = 0;i<questionTypes.length;i++){
                List<Question> simpleQuestionList = questionServiceImp.selectQuestionByCondition(template.getYuliu1(), questionTypes[i], "公有", CommonData.COMMON_QUESTION_LEVEL_SIMPLE);
                List<Question> mediumQuestionList = questionServiceImp.selectQuestionByCondition(template.getYuliu1(),  questionTypes[i], "公有", CommonData.COMMON_QUESTION_LEVEL_MEDIUM);
                List<Question> difficultQuestionList = questionServiceImp.selectQuestionByCondition(template.getYuliu1(),  questionTypes[i], "公有", CommonData.COMMON_QUESTION_LEVEL_DIFFICULT);
                Map<String,List<Question>> currentQuestionModel = new HashMap<>();
                currentQuestionModel.put(CommonData.COMMON_QUESTION_LEVEL_SIMPLE,simpleQuestionList);
                currentQuestionModel.put(CommonData.COMMON_QUESTION_LEVEL_MEDIUM,mediumQuestionList);
                currentQuestionModel.put(CommonData.COMMON_QUESTION_LEVEL_DIFFICULT,difficultQuestionList);
                currentTemplateQuestionCache.put(questionTypes[i],currentQuestionModel);
            }
            //循环走完 当前模板需要的所有试题类型的数据都在这里
            //接下来就是每一个学生 抽题 生成考卷 存储
            //如果这里用一个循环处理这场考试的所有学生的考卷生成 性能有点慢 所以利用多线程处理(线程都快忘了)
            //因为线程是根据cpu处理器的个数决定 因为电脑不好 处理器不多好像就6个 所以 这里用3个线程处理这些学生
            //因为这个线程类就是为了处理学生考卷的生成 所以 写一个内部类就可以
            //需要分配学生给对应的线程  需要获取当前模板的考试的学生
            List<Long> longs = student_examServiceImp.selectStudentIdsByExamId(examId);
            List<Student> studentList = studentServiceImp.selectStudentByIds(longs);
            List<List<Student>> group_list = new ArrayList<>();
            int index = 0;
            int group_count=2;
            for (int i = 0;i<group_count;i++){
                group_list.add(new ArrayList<Student>());
            }
            for (Student student : studentList){
                group_list.get(index).add(student);
                index = index==group_count-1?0:++index;
            }
            //分配完后 就启动线程处理了
            List<Future> futureList = new ArrayList<>();//用于装载线程池submit产生的回调 统一做判断
            for (int i = 0;i<=group_count-1;i++){
                futureList.add(executorService.submit(new DynamicPageHandler(currentTemplateQuestionCache, allQuestionList, group_list.get(i), exam, template)));
            }

//            new Thread(new DynamicPageHandler(currentTemplateQuestionCache,allQuestionList,group_list.get(0),exam,template)).start();
//            new Thread(new DynamicPageHandler(currentTemplateQuestionCache,allQuestionList,group_list.get(1),exam,template)).start();
//            new Thread(new DynamicPageHandler(currentTemplateQuestionCache,allQuestionList,group_list.get(2),exam,template)).start();
//          因为不知道为什么启用3额线程只有最后一个线程能够成功 单线程都没有问题  解决不了  所以这里就用单线程处理所有的考卷
            //解决了  问题在于 线程对象中 的属性不能用static
            w:while (true) {
                for (int i =0;i< futureList.size();i++){
                    Future future = futureList.get(i);
                    if (!future.isDone()){
                        //当前这个线程还没有执行完  不用再判断后面的
                        continue w;
                    }
                }
                break w;
            }
            return true;
        }

    }
    private  class DynamicPageHandler implements Runnable{//生成考卷我们没有做如果没有对应个数的考题判断  意思就是数据库一定有对应模板需要的考题
        private  Map<String,Map<String,List<Question>>> questionCache;
        private  List<Student> students;
        private  Exam exam;
        private  Template template;

        private  Random random = new Random();
        //存放这当前模板的question1-question5的格式
        private  List<String> templateQuestionTypes;
        //单选题  多选题 判断题 填空题 综合题
        private String[] questionTypes = new String[]{CommonData.COMMON_QUESTION1,CommonData.COMMON_QUESTION2,CommonData.COMMON_QUESTION3,CommonData.COMMON_QUESTION4,CommonData.COMMON_QUESTION5};
        //简单  中等  困难
        private  String[] questionLevel = new String[]{CommonData.COMMON_QUESTION_LEVEL_SIMPLE,CommonData.COMMON_QUESTION_LEVEL_MEDIUM,CommonData.COMMON_QUESTION_LEVEL_DIFFICULT};
        public void run(){
            //处理生成逻辑
            //需要将当前所有的学生产生一个文件并存储到指定的文件中 每一个学生考卷存储格式：学号_姓名.txt`
            //用来装载一个考生的抽取试题
                for (int i = 0;i<students.size();i++){
                    List<Question> currentStudentQuestionList = new ArrayList<>();//用于一个学生的试题装载
                    Student student = students.get(i);
                    //单个学生的动态试题抽取  因为每一个学生都是遵循当前动态模板的格式进行抽取试题 意思就是每一个学生都需要question1--question5的抽取
                    for(int j = 0;j< questionTypes.length;j++){//在处理填空题的时候可能有问题  我们是按照题号抽取填空题 不是按照填空题的空数抽取
                        //每一次获取一个试题的存储格式
                        String[] format = templateQuestionTypes.get(j).split(CommonData.COMMON_SEPARATOR);//.
                        String currentQuestionType = questionTypes[j];
                        //上面的数组长度永远的4  依次是 分值  简单个数 中等个数 困难个数
                        for(int x = 0;x< questionLevel.length;x++){//3
                            //这个循环是处理当前试题的简单 中等 困难的试题抽取
                            String questionType = questionLevel[x];
                            Map<String, List<Question>> typeList = questionCache.get(currentQuestionType);//比如 这里转载单选题 的 简单 中等 困难三个类型的试题 并且是当前课程
                            List<Question> levelList = typeList.get(questionLevel[x]);
                            Set<Integer> randomNumberBox = new HashSet<>();//用来装载每一次随机的数字 判断是否重复
                            for (int y = 0;y<Integer.parseInt(format[x+1]);y++){
                                //当前等级试题的抽取
                                int random_number = random.nextInt(levelList.size());
                                while (randomNumberBox.contains(random_number)){//这里会出现问题 当我们数据库当前试题个数小于抽取个数  就会进入死循环 (所以要保证数据库有足够的题目抽取)
                                    random_number = random.nextInt(levelList.size());
                                }
                                randomNumberBox.add(random_number);
                                Question question = levelList.get(random_number);
                                question.setYuliu1(format[0]);
                                currentStudentQuestionList.add(question);
                            }
                            randomNumberBox.clear();
                        }
                    }
                    //循环到这里 表示当前线程处理的单个学生 试题已经抽取完毕 需要写入文件
                    File file = new File(CommonData.COMMON_EXAM_PATH+exam.getName()+"/"+student.getCode()+"_"+student.getSname()+".txt");
                    try {
                        file.createNewFile();
                        FileWriter fileWriter = new FileWriter(file);
                        for (int j = 0;j<currentStudentQuestionList.size();j++){
                            Question question = currentStudentQuestionList.get(j);
                            fileWriter.write("第"+(j+1)+"题类型:"+question.getType()+CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                            fileWriter.write("第"+(j+1)+"题分值:" + question.getYuliu1() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                            fileWriter.write("第"+(j+1)+"题题干:" + question.getSubject() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                            fileWriter.write("第"+(j+1)+"题选项:" + question.getOptions() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                            fileWriter.write("第"+(j+1)+"题答案:" + question.getAnswer() + CommonData.COMMON_QUESTION_INNER_SEPARATOR);
                            fileWriter.write(CommonData.COMMON_QUESTION_SEPARATOR);
                        }
                        fileWriter.flush();
                        fileWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    currentStudentQuestionList.clear();
                    ExamControllerT.this.student_examServiceImp.updateAllRelationStudentPage_path(exam.getId(),student.getId(),file.getAbsolutePath());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            //到此当前线程处理的单个学生试卷已经生成  需要将存储路径添加到student_exam中


        }

        public DynamicPageHandler(Map<String,Map<String,List<Question>>> questionCache,List<String> templateQuestionTypes,List<Student> students,Exam exam,Template template) {
            this.students = students;
            this.template = template;
            this.exam = exam;
            this.questionCache = questionCache;
            this.templateQuestionTypes = templateQuestionTypes;
        }
    }
    @RequestMapping("removeExamPage")
    @ResponseBody
    public void removeExamPage(Long examId){
        Exam exam = examServiceImp.selectExamById(examId);
        student_examServiceImp.removeExamPage(exam);
    }
    @RequestMapping("removeExam")
    @ResponseBody
    public void removeExam(Long examId){
        Exam exam = examServiceImp.selectExamById(examId);
        student_examServiceImp.removeExamPage(exam);
        examServiceImp.removeExamByExamId(examId);
    }
    @RequestMapping("releaseExam")
    @ResponseBody
    public boolean releaseExam(Long examId){
        //查看单签考试是否生成全部学生考卷
        int count = student_examServiceImp.examGeneratePaperInfo(examId);
        if (count>0){
            //表示当前考试存在还没有生成考卷的学生
            return false;
        }
        examServiceImp.releaseExam(examId);
        return true;
    }
    @RequestMapping("abandonExam")
    @ResponseBody
    public void abandonExam(Long examId){
        examServiceImp.updateExamStatus(examId,"丢弃");
    }
    @ResponseBody
    @RequestMapping("finishExam")
    public boolean finishExam(Long examId){
        //这个结束考试一般是针对于时长考试  区间考试学生考试到点自然结束
        //这里设置下考试状态就可以 但是还有一个问题 什么呢？ 当这个考试结束时 我们应该将这场考试相关学生状态也设置一下  比如：考试结束 学生如果是考试中-->已结束  未考试-->缺考
        //这个设置也是需要在区间考试做一下
        Exam exam = examServiceImp.selectExamById(examId);
        if (exam.getDuration()!=null){
            examServiceImp.updateExamStatus(examId,"已完成");
            //需要将这个考试相关学生状态设置一下
            student_examServiceImp.finishExamToUpdateFinishStatus(examId);
            student_examServiceImp.finishExamToUpdateMissStatus(examId);
            //处理考试卷子的客观题分数 单选 多选 填空
            List<Student_Exam> studentExams = student_examServiceImp.selectExamAllStudentByExamId(examId);
            new Thread(new HandleExamStudentScore(studentExams)).start();
            return true;
        }else {
            return false;
        }
    }
    private class HandleExamStudentScore implements Runnable{
        private List<Student_Exam> studentExams;
        private HandleExamStudentScore(List<Student_Exam> studentExams){
            this.studentExams = studentExams;
        }
        @Override
        public void run() {
            for(Student_Exam student_exam:studentExams){
                examServiceImp.handleOneStudentExamPaperScore(student_exam);
            }
        }
    }
    @RequestMapping("updateExamPaper.html")
    public String updateExamPaper(Long examId,Model model){
        List<ExamPaperDTO> examPaperDTOS = examServiceImp.selectFinishExamClass(examId);
        model.addAttribute("examClass",examPaperDTOS);
        model.addAttribute("exam",examServiceImp.selectExamById(examId));

        return "exam/updateExamPaper";
    }
    @RequestMapping("examPaperStudentList.html")
    public String examPaperStudentList(Long examId,String className,Model model){
        Exam exam = examServiceImp.selectExamById(examId);
        model.addAttribute("exam",exam);//这个对象就是为了考题提交 页面判断
        List<ExamStudentDTO> examStudentDTOS = examServiceImp.selectExamStudentList(examId, className);
        model.addAttribute("examStudentList",examStudentDTOS);
        return "exam/examPaperStudentList";
    }
    @ResponseBody
    @RequestMapping("updateExamStudentState")
    public void updateExamStudentState(Long examId,Long studentId,String status){
        student_examServiceImp.updateStudentStatus(examId,studentId,status);
    }
    @RequestMapping("readOneStudentExamPaper.html")
    public String readOneStudentExamPaper(Long examId,Long studentId,Model model){
        ExamStudentDTO examStudentDTO = examServiceImp.selectOneExamStudent(examId, studentId);
        model.addAttribute("examStudentDTO",examStudentDTO);
        Student_Exam student_exam = student_examServiceImp.selectStudentExamByExamAndStudentId(examId, studentId);
        model.addAttribute("studentExam",student_exam);
        List<QuestionVO> questionVOS = student_examServiceImp.generateExamPaper(student_exam.getPage_path());
        model.addAttribute("questions",questionVOS);
        StudentExamAnswerVO studentExamAnswerVO = student_examServiceImp.parseExamAnswer(student_exam);
        model.addAttribute("studentExamAnswerVO",studentExamAnswerVO);
//        examServiceImp.handleOneStudentExamPaperScore(student_exam);
        String yuliu2 = student_exam.getYuliu2();
        model.addAttribute("selfAnswerScores",Arrays.asList(yuliu2.split(CommonData.COMMON_SEPARATOR)));
        //前端需要填空题和综合题的批阅信息
        List<String> reviewList = student_examServiceImp.handleExamReviewInfo(questionVOS, student_exam);
        model.addAttribute("reviewList",reviewList);
        Exam exam = examServiceImp.selectExamById(examId);
        model.addAttribute("exam",exam);
        return "exam/studentReadExamPaper";
    }
    @RequestMapping("flushExamStudentScore")
    @ResponseBody
    public void flushExamStudentScore(Student_Exam student_exam){
        student_examServiceImp.flushExamStudentScore(student_exam);
    }
    @ResponseBody
    @RequestMapping("reviewSave")
    public void reviewSave(@RequestParam Map<String,String> map){
        student_examServiceImp.reviewSave(map);
    }
    @RequestMapping("examPaperSubmit")
    @ResponseBody
    public void examPaperSubmit(Long examId){
        examServiceImp.examPaperSubmit(examId);
    }
}
